import sys
import pygame
import pygame.freetype
import random
import time
from resource import *
from stagemap import *
from tools import *

pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("坦克大战")
fclock = pygame.time.Clock()
BLACK = 0, 0, 0
WHITE = 255, 255, 255
GREY = 125, 125, 125
start_sound = pygame.mixer.Sound('sound/start.wav')
start_sound.set_volume(0.5)
home_blast_sound = pygame.mixer.Sound('sound/blast.wav')
home_blast_sound.set_volume(0.5)
hit_sound = pygame.mixer.Sound('sound/hit.wav')
hit_sound.set_volume(0.5)
get_tank = pygame.mixer.Sound('sound/GetBonus.wav')
get_tank.set_volume(0.5)
# 坦克爆炸音效
bang_sound = pygame.mixer.Sound('sound/bang.wav')
bang_sound.set_volume(0.1)
font = pygame.freetype.Font('Fonts/PressStart2P-Regular.ttf', 20)
stage = 0
stage_pass = False
# 创建玩家的老家
home_group = pygame.sprite.Group()
home = Home()
home_group.add(home)
home_reinforce = False
# 创建玩家的坦克精灵
player_group = pygame.sprite.Group()
player1 = PlayerTank(1)
player2 = PlayerTank(2)
player2.rect.centerx = wall_size * 17
player2.rect.bottom = height
title_image = pygame.image.load('images/others/title.png')
title_rect = title_image.get_rect()
title_rect.centerx = (width + wall_size * 4) / 2
title_rect.top = height
game_over_image = pygame.image.load('images/others/gameover.png')
game_over_rect = game_over_image.get_rect()
game_over_rect.centerx = width / 2
game_over_rect.top = height

selecttank_image = pygame.image.load('images/others/selecttank.png')
selecttank_rect = selecttank_image.get_rect()
select = 1
selecttank_rect.right = 270
selecttank_rect.y = 262
# 敌方坦克精灵组
enemy_group = pygame.sprite.Group()
ENEMY_EVENT = pygame.USEREVENT
pygame.time.set_timer(ENEMY_EVENT, 3000)
ENEMY_FIRE_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(ENEMY_FIRE_EVENT, 1500)
# 每一关总共有20辆敌方坦克
enemy_num = 20
born_position = [(wall_size, wall_size), (wall_size * 13, wall_size), (wall_size * 25, wall_size)]
born_index = 1
enemy_index = 0
food_group = pygame.sprite.Group()
clock_start = 0
clock_end = 0
enemy_pause = False
destroy_group = pygame.sprite.Group()


def show_stage():
    screen.fill(GREY)
    stage_text, stage_rect = font.render('STAGE  %d' % stage, fgcolor=BLACK, size=20)
    stage_rect.centerx = (width + wall_size * 4) / 2
    stage_rect.centery = height / 2
    screen.blit(stage_text, stage_rect)
    pygame.display.update()
    pygame.time.delay(2000)


def next_stage():
    """过关，进入下一关"""
    global stage
    global start
    global enemy_num
    global stage_pass
    global born_index
    global enemy_index
    global enemy_pause
    stage += 1
    if stage <= len(all_map):
        enemy_num = 20
        born_index = 1
        enemy_index = 0
        stage_pass = False
        enemy_pause = False
        food_group.empty()
        map_init(stage)
        start_sound.play()
        show_stage()
        for p in player_group:
            p.reset()
    else:
        stage_pass = False
        stage = 0
        for p in player_group:
            p.level = 0
            p.life = 3
            p.upgrade()
            p.reset()
        player_group.empty()
        title_rect.centerx = (width + wall_size * 4) / 2
        title_rect.top = height
        start = False


def menu():
    """选择模式菜单"""
    global select
    global start
    global stage
    screen.fill(BLACK)
    screen.blit(title_image, title_rect)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_f:
                if title_rect.y == 0:
                    if select < 2:
                        select += 1
                    elif select >= 2:
                        select = 1
                else:
                    title_rect.y = 0
            elif event.key == pygame.K_h:
                if title_rect.y == 0:
                    if select == 1:
                        player_group.add(player1)
                    elif select == 2:
                        player_group.add(player1, player2)
                    next_stage()
                    start = True
                else:
                    title_rect.y = 0
    # 选择菜单从下往上飞入
    if title_rect.y > 0:
        title_rect.y -= 180 / fps
    else:
        selecttank_rect.y = 232 + select * 32
        screen.blit(selecttank_image, selecttank_rect)


def key_event():
    global enemy_num
    global born_index
    global pause
    global enemy_index
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN and home.active:
            if event.key == pygame.K_h:
                pause = True
            if player1.active:
                if event.key == pygame.K_j:
                    player1.fire()
                # 坦克转向的时候位置总是控制在墙体宽度的整数倍
                if event.key == pygame.K_w:
                    if player1.rect.x % wall_size:
                        player1.rect.x = round(player1.rect.x / wall_size) * wall_size
                if event.key == pygame.K_s:
                    if player1.rect.x % wall_size:
                        player1.rect.x = round(player1.rect.x / wall_size) * wall_size
                if event.key == pygame.K_a:
                    if player1.rect.y % wall_size:
                        player1.rect.y = round(player1.rect.y / wall_size) * wall_size
                if event.key == pygame.K_d:
                    if player1.rect.y % wall_size:
                        player1.rect.y = round(player1.rect.y / wall_size) * wall_size
            if select == 2 and player2.active:
                if event.key == pygame.K_KP1:
                    player2.fire()
                # 坦克转向的时候位置总是控制在墙体宽度的整数倍
                if event.key == pygame.K_UP:
                    if player2.rect.x % wall_size:
                        player2.rect.x = round(player2.rect.x / wall_size) * wall_size
                if event.key == pygame.K_DOWN:
                    if player2.rect.x % wall_size:
                        player2.rect.x = round(player2.rect.x / wall_size) * wall_size
                if event.key == pygame.K_LEFT:
                    if player2.rect.y % wall_size:
                        player2.rect.y = round(player2.rect.y / wall_size) * wall_size
                if event.key == pygame.K_RIGHT:
                    if player2.rect.y % wall_size:
                        player2.rect.y = round(player2.rect.y / wall_size) * wall_size
        if event.type == pygame.KEYUP and home.active:
            if event.key == pygame.K_w:
                player1.frame = 1
                player1.driving_sound.stop()
            elif event.key == pygame.K_s:
                player1.frame = 1
                player1.driving_sound.stop()
            elif event.key == pygame.K_a:
                player1.frame = 1
                player1.driving_sound.stop()
            elif event.key == pygame.K_d:
                player1.frame = 1
                player1.driving_sound.stop()

            if event.key == pygame.K_UP:
                player2.frame = 1
                player2.driving_sound.stop()
            elif event.key == pygame.K_DOWN:
                player2.frame = 1
                player2.driving_sound.stop()
            elif event.key == pygame.K_LEFT:
                player2.frame = 1
                player2.driving_sound.stop()
            elif event.key == pygame.K_RIGHT:
                player2.frame = 1
                player2.driving_sound.stop()
        if event.type == ENEMY_EVENT:
            if len(enemy_group) < 5 and enemy_num > 0:
                enemy = EnemyTank(stage_enemy[stage - 1][enemy_index])
                enemy.rect.center = born_position[born_index]
                born_index += 1
                enemy_index += 1
                if born_index == 3:
                    born_index = 0
                enemy_num -= 1
                enemy.direction = random.choice(['down', 'left', 'right'])
                enemy_group.add(enemy)
        elif event.type == ENEMY_FIRE_EVENT:
            for e in enemy_group:
                if not enemy_pause:
                    e.fire()


def main():
    while True:
        screen.fill(GREY)
        pygame.draw.rect(screen, BLACK, (0, 0, width, height))
        if start:
            if pause:
                game_pause()
            else:
                key_event()
                check_collide()
                update()
        else:
            menu()
        if stage_pass:
            next_stage()

        pygame.display.update()
        fclock.tick(fps)


def check_collide():
    """碰撞检测"""
    global select
    global start
    global gameover
    global stage_pass
    global clock_start
    global clock_end
    global enemy_pause
    global home_reinforce
    for p in player_group:
        if p.active and home.active:
            p.move()
        # 玩家坦克遇到障碍物
        hit_home = pygame.sprite.collide_rect(p, home)
        hit_wall = pygame.sprite.spritecollide(p, wall_group, False)
        if hit_home:
            check_hit(p, home)
        elif hit_wall:
            for w in hit_wall:
                if not w.crossover:
                    check_hit(p, w)
        # 吃道具
        hit_food = pygame.sprite.spritecollide(p, food_group, False)
        if hit_food:
            for fd in hit_food:
                if fd.food_num == 1:
                    for e1 in enemy_group:
                        e1.active = False
                        fd.kill()
                elif fd.food_num == 2:
                    enemy_pause = True
                    clock_start = time.time()
                    fd.kill()
                elif fd.food_num == 3:
                    p.level = 3
                    p.upgrade()
                    fd.kill()
                elif fd.food_num == 4:
                    home_reinforce = True
                    fd.kill()
                elif fd.food_num == 5:
                    p.protected = True
                    p.protective_cover.time = 0
                    fd.kill()
                elif fd.food_num == 6:
                    if p.level < 3:
                        p.level += 1
                        p.upgrade()
                    fd.kill()
                elif fd.food_num == 7:
                    p.life += 1
                    get_tank.play()
                    fd.kill()
        # 子弹碰撞
        for b in p.bullets:
            if b.active:
                b.move()
                # 子弹击中障碍物
                shoot_wall = pygame.sprite.spritecollide(b, wall_group, False)
                if shoot_wall:
                    for w in shoot_wall:
                        if w.solid != 0:
                            if w.solid == 1 or b.level > 2:
                                b.active = False
                                w.kill()

                            elif w.solid > 1:
                                hit_sound.play()
                                b.active = False
                # 子弹击中老家
                shoot_home = pygame.sprite.spritecollide(b, home_group, False)
                if shoot_home:
                    b.active = False
                    for p2 in player_group:
                        p2.driving_sound.stop()
                    home.active = False
                    explosion = DestroyEffects(home)
                    destroy_group.add(explosion)
                    gameover = True
                    home_blast_sound.play()
                    pygame.mixer.music.stop()
                    # break
                shoot_enemy = pygame.sprite.spritecollide(b, enemy_group, False)
                if shoot_enemy:
                    b.active = False
                    for et in shoot_enemy:
                        et.blood -= 1
                        if et.blood > 0:
                            hit_sound.play()
                        else:
                            if et.food:
                                food_num = random.randint(1, 7)
                                food = Food(food_num)
                                food_group.add(food)
                            bang_sound.play()
                            et.active = False
    if enemy_pause:
        clock_end = time.time()
        if clock_end - clock_start > 12:
            enemy_pause = False
    for e in enemy_group:
        if e.active:
            if not enemy_pause:
                e.move()
            # 敌方坦克遇到障碍物
            hit_home = pygame.sprite.collide_rect(e, home)
            if hit_home:
                check_hit(e, home)
                e.reselect_direction()
            for w in wall_group:
                if pygame.sprite.collide_rect(e, w):
                    if not w.crossover:
                        check_hit(e, w)
                        e.reselect_direction()
                        break
        # 子弹碰撞
        for b in e.bullets:
            if b.active:
                b.move()
                # 子弹击中障碍物
                shoot_wall = pygame.sprite.spritecollide(b, wall_group, False)
                if shoot_wall:
                    for w in shoot_wall:
                        if w.solid == 1:
                            b.active = False
                            w.kill()
                        elif w.solid > 1:
                            hit_sound.play()
                            b.active = False

                # 子弹击中老家
                shoot_home = pygame.sprite.spritecollide(b, home_group, False)
                if shoot_home and home.active:
                    b.active = False
                    for p3 in player_group:
                        p3.driving_sound.stop()
                    home.active = False
                    explosion = DestroyEffects(home)
                    destroy_group.add(explosion)
                    gameover = True
                    home_blast_sound.play()
                    pygame.mixer.music.stop()
                    break
                # 敌人子弹击中玩家坦克
                shoot_player = pygame.sprite.spritecollide(b, player_group, False)
                if shoot_player:
                    for p in shoot_player:
                        if p.active:
                            b.active = False
                            if not p.protected:
                                p.blood -= 1
                                if p.blood > 0:
                                    hit_sound.play()
                                    p.level -= 1
                                    p.upgrade()
                                else:
                                    bang_sound.play()
                                    p.life -= 1
                                    p.active = False
                                    explosion = DestroyEffects(p)
                                    destroy_group.add(explosion)
                                    p.level = 0
                                    p.upgrade()
                                    if p.life > 0:
                                        p.reset()
                    # break
                # 和玩家子弹相互抵消
                for p in player_group:
                    shoot_bullet = pygame.sprite.spritecollide(b, p.bullets, True)
                    if shoot_bullet:
                        b.kill()
        # 地方坦克相遇后改变方向
        for e2 in enemy_group:
            if e2 is not e:
                if pygame.sprite.collide_rect(e, e2):
                    check_hit(e, e2)
                    e.reselect_direction()
    if select == 1:
        if player1.life == 0:
            gameover = True
    elif select == 2:
        if player1.life == 0 and player2.life == 0:
            gameover = True
    if len(enemy_group) == 0 and enemy_num == 0:
        for p in player_group:
            p.driving_sound.stop()
        stage_pass = True


def check_hit(object1, object2):
    """
    检测遇到障碍物能否穿过
    :param object1: 碰撞的精灵
    :param object2: 被撞的精灵
    :return:
    """
    if object1.direction == 'up':
        object1.rect.y = object2.rect.bottom
    elif object1.direction == 'down':
        object1.rect.bottom = object2.rect.y
    elif object1.direction == 'left':
        object1.rect.x = object2.rect.right
    elif object1.direction == 'right':
        object1.rect.right = object2.rect.x


def draw_ui():
    """绘制右侧界面的信息"""
    global enemy_num
    global select
    # 生成剩余敌方坦克的二维列表
    enemy_list = [[0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0],
                  [0, 0]
                  ]
    image_num = 0
    if enemy_num > 0:
        for row in range(10):
            for col in range(2):
                if image_num >= enemy_num:
                    break
                image_num += 1
                enemy_list[row][col] = 1
    # 根据剩余敌方坦克的列表绘制到屏幕右侧
    for i in range(10):
        r = enemy_list[i]
        for j in range(2):
            c = r[j]
            if c == 1:
                enemy_num_rect.x = width + wall_size * (j + 1)
                enemy_num_rect.y = wall_size * (i + 1)
                screen.blit(enemy_num_image, enemy_num_rect)
            else:
                break
    if player1.life > 0:
        player1_remain = player1.life - 1
    else:
        player1_remain = 0
    player1_num_text, player1_num_rect = font.render('%d' % player1_remain, fgcolor=BLACK, size=16)
    player1_num_rect.x = wall_size * 28 - 8
    player1_num_rect.y = wall_size * 16
    stage_num_image, stage_num_rect = font.render('%d' % stage, fgcolor=BLACK, size=16)
    stage_num_rect.x = wall_size * 28 - 8
    stage_num_rect.y = wall_size * 22
    screen.blit(IP_image, IP_rect)
    screen.blit(player_life_image, player1_life_rect)
    screen.blit(player1_num_text, player1_num_rect)
    screen.blit(flag_image, flag_rect)
    screen.blit(stage_num_image, stage_num_rect)
    if select == 2:
        if player2.life > 0:
            player2_remain = player2.life - 1
        else:
            player2_remain = 0
        player2_num_text, player2_num_rect = font.render('%d' % player2_remain, fgcolor=BLACK, size=16)
        player2_num_rect.x = wall_size * 28 - 8
        player2_num_rect.y = wall_size * 19
        screen.blit(IIP_image, IIP_rect)
        screen.blit(player_life_image, player2_life_rect)
        screen.blit(player2_num_text, player2_num_rect)


def update():
    """更新游戏中所有元素的显示"""
    global home_reinforce
    # 绘制右侧信息
    draw_ui()
    # 绘制大本营
    if home_reinforce:
        reinforce_home()
        home_reinforce = False
    if home.active:
        screen.blit(home.image1, home.rect)
    else:
        screen.blit(home.image2, home.rect)
    # 绘制玩家坦克
    for p in player_group:
        if p.active:
            screen.blit(p.image, p.rect)
            if p.protected:
                p.protective_cover.draw(p.rect)
                p.protected_end = time.time()
                if p.protective_cover.time >= 11:
                    p.protected = False
                    p.protective_cover.time = 0
        # 绘制子弹
        for bullet in p.bullets:
            if bullet.active:
                pygame.draw.rect(screen, WHITE, bullet.rect)
            else:
                explosion = DestroyEffects(bullet)
                destroy_group.add(explosion)
                bullet.kill()

    # 绘制敌方坦克
    for e in enemy_group:
        if e.active:
            screen.blit(e.image, e.rect)
        else:
            explosion = DestroyEffects(e)
            destroy_group.add(explosion)
            e.kill()
        # 绘制敌方坦克子弹
        for b in e.bullets:
            if b.active:
                pygame.draw.rect(screen, WHITE, b.rect)
            else:
                explosion = DestroyEffects(b)
                destroy_group.add(explosion)
                b.kill()
    # 绘制地图
    for w in wall_group:
        screen.blit(w.image, w.rect)
    # 绘制食物
    for f in food_group:
        screen.blit(f.image, f.rect)
    if gameover:
        game_over()

    # 绘制爆炸效果
    for explosion in destroy_group:
        explosion.show()


def game_pause():
    global pause
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_h:
                pause = False
    for p in player_group:
        if p.active:
            screen.blit(p.image, p.rect)
            if p.protected:
                screen.blit(p.protective_cover.image, p.rect)
        for b in p.bullets:
            if b.active:
                pygame.draw.rect(screen, WHITE, b.rect)
    for e in enemy_group:
        if e.active:
            screen.blit(e.image, e.rect)
        for b in e.bullets:
            if b.active:
                pygame.draw.rect(screen, WHITE, b.rect)
    # 绘制地图
    for w in wall_group:
        screen.blit(w.image, w.rect)
    draw_ui()
    screen.blit(home.image1, home.rect)
    for f in food_group:
        screen.blit(f.image, f.rect)
    # 绘制爆炸效果
    for explosion in destroy_group:
        explosion.show()


def game_over():
    screen.blit(game_over_image, game_over_rect)
    if game_over_rect.centery > height / 2:
        game_over_rect.y -= 180 / fps


if __name__ == '__main__':
    main()
